﻿using Serilog;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Polly;
using Polly.Retry;
using Polly.Contrib.WaitAndRetry;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Polly.Wrap;

namespace Helpers
{
    public class WeedFsHttpClient
    {
        /// <summary>
        /// 单例
        /// </summary>
        public static WeedFsHttpClient Instance => DiHelper.GetRequiredService<WeedFsHttpClient>();
        private readonly HttpClient _httpClient;
        private static AsyncPolicyWrap<bool> _retryPolicy;

        static WeedFsHttpClient()
        {
            var waitAndRetry = Policy.HandleResult<bool>(x => !x)
            .WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 3, fastFirst: true));
            var circuitBreaker = Policy.HandleResult<bool>(x => !x).CircuitBreakerAsync(3, TimeSpan.FromSeconds(60));
            var fallback = Policy.HandleResult<bool>(x => !x).FallbackAsync(true);
            _retryPolicy = Policy.WrapAsync(fallback, circuitBreaker, waitAndRetry);
        }

        public WeedFsHttpClient(HttpClient httpClient, ApiCacheOptions responseCacheOptions)
        {
            _httpClient = httpClient;
            _httpClient.BaseAddress = responseCacheOptions.WeedFsUrl;
        }

        /// <summary>
        /// 从缓存获取 null表示没缓存
        /// </summary>
        public async Task<string> GetAsync(string cacheKey)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(cacheKey)) return null;

                var hrm = new HttpRequestMessage(HttpMethod.Get, cacheKey);
                var response = await _httpClient.SendAsync(hrm).ConfigureAwait(false);
                if (response.StatusCode == System.Net.HttpStatusCode.NotFound) return null;
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"获取缓存异常,{cacheKey}");
            }
            return null;
        }

        public async Task<bool> DelAsync(string cacheKey)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(cacheKey)) return false;

                var hrm = new HttpRequestMessage(HttpMethod.Delete, cacheKey);
                var response = await _httpClient.SendAsync(hrm, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                return true;
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"删除缓存异常,{cacheKey}");
            }
            return false;
        }

        /// <summary>
        /// 查询目录列表
        /// </summary>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        public async Task<string> QueryAsync(string cacheKey)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(cacheKey)) return null;

                var hrm = new HttpRequestMessage(HttpMethod.Get, cacheKey);
                hrm.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                var response = await _httpClient.SendAsync(hrm, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"查询目录列表缓存异常,{cacheKey}");
            }
            return null;
        }

        /// <summary>
        /// 增加重试
        /// </summary>
        public async Task<bool> AddAsync(string cacheKey, string value, string ttl = "24h") => await _retryPolicy.ExecuteAsync(() => AddNoRetryAsync(cacheKey, value, ttl)).ConfigureAwait(false);

        /// <summary>
        /// 添加缓存
        /// </summary>
        private async Task<bool> AddNoRetryAsync(string cacheKey, string value, string ttl = "24h")
        {
            try
            {
                if (string.IsNullOrWhiteSpace(value)) return true;

                var content = new MultipartFormDataContent();
                content.Add(new StringContent(value), "file");
                var requestUri = $"{cacheKey}?ttl={ttl}";
                var resp = await _httpClient.PostAsync(requestUri, content).ConfigureAwait(false);
                if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    Log.Error($"AddNoRetryAsync异常,{cacheKey}");
                    return true;
                }
                var result = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
                if (!Regex.IsMatch(result, "\"(?:name|size)\""))
                {
                    Log.Warning($"设置缓存失败,{cacheKey},{result}");
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "设置缓存异常");
                return false;
            }
        }

        /// <summary>
        /// 获取或者添加缓存(字符串""也会缓存)
        /// </summary>
        /// <param name="cacheKey">缓存路径,例如/xxx/xx</param>
        /// <param name="action"></param>
        /// <returns></returns>
        public async Task<T> GetOrAddAsync<T>(string cacheKey, Func<CacheOptions<T>, Task<T>> action) where T : class
        {
            var dataStr = await GetAsync(cacheKey).ConfigureAwait(false);
            T data = default;
            if (dataStr != null)
            {
                var cacheOptions = new CacheOptions<T>();
                data = await action(cacheOptions).ConfigureAwait(false);
                if (data != null)
                {
                    if (cacheOptions.CheckResult == null || cacheOptions.CheckResult(data))
                    {
                        await AddAsync(cacheKey, data is string ? data as string : JsonConvert.SerializeObject(data), cacheOptions.Ttl).ConfigureAwait(false);
                    }
                }
            }
            else
            {
                data = data is string ? dataStr as T : JsonConvert.DeserializeObject<T>(dataStr);
            }
            return data;
        }

        /// <summary>
        /// 获取或者添加缓存(字符串""也会缓存)
        /// </summary>
        public async Task<string> GetOrAddStringAsync(string cacheKey, Func<CacheOptions<string>, Task<string>> action)
        {
            var dataStr = await GetAsync(cacheKey).ConfigureAwait(false);
            if (dataStr == null)
            {
                var cacheOptions = new CacheOptions<string>();
                dataStr = await action(cacheOptions).ConfigureAwait(false);
                if (dataStr != null)
                {
                    if (cacheOptions.CheckResult == null || cacheOptions.CheckResult(dataStr))
                    {
                        await AddAsync(cacheKey, dataStr, cacheOptions.Ttl).ConfigureAwait(false);
                    }
                }
            }
            return dataStr;
        }

        /// <summary>
        /// 获取缓存数据
        /// </summary>
        public static async Task<string> GetOrCreateAsync(WeedGetOrCreateOptions options)
        {
            string content = null;
            //获取缓存
            if (!options.isRefresh)
            {
                content = await WeedFsHttpClient.Instance.GetAsync(options.cacheKey).ConfigureAwait(false);
                if (!string.IsNullOrWhiteSpace(content)) return content;
            }

            //获取数据
            if (options.getDatafunc == null) throw new ArgumentNullException("options.getDatafunc");
            content = await options.getDatafunc().ConfigureAwait(false);

            //检查数据
            if (!string.IsNullOrWhiteSpace(content) && (options.checkFunc == null || options.checkFunc(content)))
            {
                //更新缓存
                await WeedFsHttpClient.Instance.AddAsync(options.cacheKey, content, options.Ttl).ConfigureAwait(false);
            }

            return content;
        }

        public class CacheOptions<T>
        {
            /// <summary>
            /// 缓存时间
            /// </summary>
            public string Ttl { get; set; } = ApiCacheOptions.DefaultTtl;
            /// <summary>
            /// 检查结果 返回true才会缓存
            /// </summary>
            public Func<T, bool> CheckResult { get; set; }
        }
    }

    public class WeedGetOrCreateOptions
    {
        public WeedGetOrCreateOptions()
        {
        }

        public WeedGetOrCreateOptions(string cacheKey)
        {
            this.cacheKey = cacheKey;
        }

        public WeedGetOrCreateOptions(string cacheKey,bool isRefresh)
        {
            this.cacheKey = cacheKey;
            this.isRefresh = isRefresh;
        }

        /// <summary>
        /// 缓存路径
        /// </summary>
        public string cacheKey { get; set; }
        /// <summary>
        /// 是否刷新缓存数据
        /// </summary>
        public bool isRefresh { get; set; } = false;
        /// <summary>
        /// 获取数据
        /// </summary>
        public Func<Task<string>> getDatafunc { get; set; }
        /// <summary>
        /// 验证结果,true-验证通过
        /// </summary>
        public Func<string, bool> checkFunc { get; set; } = null;
        /// <summary>
        /// 缓存时间,相同的cacheKey的缓存时间需要一样
        /// </summary>
        public string Ttl { get; set; } = ApiCacheOptions.DefaultTtl;
    }
}
